在Lua中循环Require是如何处理的?
在 Lua 中,当多个脚本文件循环 require
时(例如 A 依赖 B,B 又依赖 A),最后 require
的值为 true
是由于 Lua 的 模块加载机制 和 避免无限循环 的设计导致的:
- 模块加载状态跟踪:Lua 使用
package.loaded
表跟踪已加载的模块。 - 占位符机制:当开始加载模块 A 时,会先在
package.loaded
中设置A = true
(临时占位符)。 - 循环检测:若在加载 A 的过程中遇到
require B
,而 B 又尝试require A
:- 此时
package.loaded[A]
已存在(值为true
)。 - Lua 会直接返回这个占位符值
true
,避免无限循环。
- 此时
- 最终值替换:当 A 完全加载后,其返回值会替换占位符(但循环依赖的模块已获取了占位值
true
)。
在lua5.1中,出现循环require会直接报错(这边我们不讨论在5.1下的情况),如果报错是非常容易排查的,如下图:
代码案例演示
假设有两个文件互相依赖:
执行结果:
1 | Start loading A |
关键点说明
- 加载流程:
main.lua
执行require "a"
,开始加载 A。- A 执行
require "b"
,开始加载 B。 - B 执行
require "a"
,此时 A 正在加载中(package.loaded[a] = true
),直接返回true
。
- 值的变化:
- B 中的
a
获取到占位符true
。 - A 加载完成后,
package.loaded["a"]
被替换为"Module A"
。 - 但 B 中已获取的
a
值不会更新(仍是true
)。
- B 中的
Lua 源码分析(以 Lua 5.4 为例)
关键函数在 loadlib.c
中的 ll_require
函数:
核心逻辑
1 | static int ll_require (lua_State *L) { |
关键步骤:
- **检查
package.loaded
**:若模块已存在,直接返回其值。 - 设置占位符:在加载前设置
package.loaded[name] = true
,标记模块正在加载。 - 处理循环依赖:当依赖模块尝试
require
当前模块时,直接返回占位符true
。 - 替换最终值:模块加载完成后,用返回值替换占位符(若未返回值,则保持
true
)。
解决方案:避免循环依赖
- 重构代码:解耦模块间的双向依赖。
- 延迟加载:在需要时再
require
(例如在函数内部调用)。1
2
3
4
5
6-- b.lua 修复版
local a
function get_a()
if not a then a = require "a" end
return a
end - 显式传递依赖:通过参数传递避免
require
。
最佳实践:模块设计应遵循 单向依赖 原则,避免循环
require
。若无法避免,需明确处理占位值true
的情况。
本文标题:在Lua中循环Require是如何处理的?
文章作者:Keyle
发布时间:2025-08-07
最后更新:2025-08-07
原始链接:https://vrast.cn/posts/60644/
版权声明:©Keyle's Blog. 本站采用署名-非商业性使用-相同方式共享 4.0 国际进行许可